/******************************************************************************
 * @file     SW_DP.c
 * @brief    CMSIS-DAP SW DP I/O
 * @version  V1.10
 * @date     20. May 2015
 *
 * @note
 * Copyright (C) 2012-2015 ARM Limited. All rights reserved.
 *
 * @par
 * ARM Limited (ARM) is supplying this software for use with Cortex-M
 * processor based microcontrollers.
 *
 * @par
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 ******************************************************************************/

#include "DAP_config.h"
#include "DAP.h"

// SW Macros

#define PIN_SWCLK_SET PIN_SWCLK_TCK_SET
#define PIN_SWCLK_CLR PIN_SWCLK_TCK_CLR

#define SW_CLOCK_CYCLE() \
	PIN_SWCLK_CLR();     \
	PIN_SWCLK_SET();

#define SW_WRITE_BIT(bit) \
	PIN_SWDIO_OUT(bit);   \
	PIN_SWCLK_CLR();      \
	PIN_SWCLK_SET();

#define SW_READ_BIT(bit)  \
	PIN_SWCLK_CLR();      \
	bit = PIN_SWDIO_IN(); \
	PIN_SWCLK_SET();

#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)

// Generate SWJ Sequence
//   count:  sequence bit count
//   data:   pointer to sequence bit data
//   return: none
#if ((DAP_SWD != 0) || (DAP_JTAG != 0))
void SWJ_Sequence(uint32_t count, const uint8_t *data)
{
	uint32_t val;
	uint32_t n;

	val = 0U;
	n = 0U;
	while (count--)
	{
		if (n == 0U)
		{
			val = *data++;
			n = 8U;
		}
		if (val & 1U)
		{
			PIN_SWDIO_TMS_SET();
		}
		else
		{
			PIN_SWDIO_TMS_CLR();
		}
		SW_CLOCK_CYCLE();
		val >>= 1;
		n--;
	}
}
#endif

#if (DAP_SWD != 0)

// SWD Transfer I/O
//   request: A[3:2] RnW APnDP
//   data:    DATA[31:0]
//   return:  ACK[2:0]
#define SWD_TransferFunction(speed) /**/                                              \
	uint8_t SWD_Transfer##speed(uint32_t request, uint32_t *data)                     \
	{                                                                                 \
		uint32_t ack;                                                                 \
		uint32_t bit;                                                                 \
		uint32_t val;                                                                 \
		uint32_t parity;                                                              \
                                                                                      \
		uint32_t n;                                                                   \
                                                                                      \
		/* Packet Request */                                                          \
		parity = 0U;                                                                  \
		SW_WRITE_BIT(1U); /* Start Bit */                                             \
		bit = request >> 0;                                                           \
		SW_WRITE_BIT(bit); /* APnDP Bit */                                            \
		parity += bit;                                                                \
		bit = request >> 1;                                                           \
		SW_WRITE_BIT(bit); /* RnW Bit */                                              \
		parity += bit;                                                                \
		bit = request >> 2;                                                           \
		SW_WRITE_BIT(bit); /* A2 Bit */                                               \
		parity += bit;                                                                \
		bit = request >> 3;                                                           \
		SW_WRITE_BIT(bit); /* A3 Bit */                                               \
		parity += bit;                                                                \
		SW_WRITE_BIT(parity); /* Parity Bit */                                        \
		SW_WRITE_BIT(0U);	  /* Stop Bit */                                          \
		SW_WRITE_BIT(1U);	  /* Park Bit */                                          \
                                                                                      \
		/* Turnaround */                                                              \
		PIN_SWDIO_OUT_DISABLE();                                                      \
		for (n = DAP_Data.swd_conf.turnaround; n; n--)                                \
		{                                                                             \
			SW_CLOCK_CYCLE();                                                         \
		}                                                                             \
                                                                                      \
		/* Acknowledge response */                                                    \
		SW_READ_BIT(bit);                                                             \
		ack = bit << 0;                                                               \
		SW_READ_BIT(bit);                                                             \
		ack |= bit << 1;                                                              \
		SW_READ_BIT(bit);                                                             \
		ack |= bit << 2;                                                              \
                                                                                      \
		if (ack == DAP_TRANSFER_OK)                                                   \
		{ /* OK response */                                                           \
			/* Data transfer */                                                       \
			if (request & DAP_TRANSFER_RnW)                                           \
			{                                                                         \
				/* Read data */                                                       \
				val = 0U;                                                             \
				parity = 0U;                                                          \
				for (n = 32U; n; n--)                                                 \
				{                                                                     \
					SW_READ_BIT(bit); /* Read RDATA[0:31] */                          \
					parity += bit;                                                    \
					val >>= 1;                                                        \
					val |= bit << 31;                                                 \
				}                                                                     \
				SW_READ_BIT(bit); /* Read Parity */                                   \
				if ((parity ^ bit) & 1U)                                              \
				{                                                                     \
					ack = DAP_TRANSFER_ERROR;                                         \
				}                                                                     \
				if (data)                                                             \
				{                                                                     \
					*data = val;                                                      \
				}                                                                     \
				/* Turnaround */                                                      \
				for (n = DAP_Data.swd_conf.turnaround; n; n--)                        \
				{                                                                     \
					SW_CLOCK_CYCLE();                                                 \
				}                                                                     \
				PIN_SWDIO_OUT_ENABLE();                                               \
			}                                                                         \
			else                                                                      \
			{                                                                         \
				/* Turnaround */                                                      \
				for (n = DAP_Data.swd_conf.turnaround; n; n--)                        \
				{                                                                     \
					SW_CLOCK_CYCLE();                                                 \
				}                                                                     \
				PIN_SWDIO_OUT_ENABLE();                                               \
				/* Write data */                                                      \
				val = *data;                                                          \
				parity = 0U;                                                          \
				for (n = 32U; n; n--)                                                 \
				{                                                                     \
					SW_WRITE_BIT(val); /* Write WDATA[0:31] */                        \
					parity += val;                                                    \
					val >>= 1;                                                        \
				}                                                                     \
				SW_WRITE_BIT(parity); /* Write Parity Bit */                          \
			}                                                                         \
			/* Idle cycles */                                                         \
			n = DAP_Data.transfer.idle_cycles;                                        \
			if (n)                                                                    \
			{                                                                         \
				PIN_SWDIO_OUT(0U);                                                    \
				for (; n; n--)                                                        \
				{                                                                     \
					SW_CLOCK_CYCLE();                                                 \
				}                                                                     \
			}                                                                         \
			PIN_SWDIO_OUT(1U);                                                        \
			return ((uint8_t)ack);                                                    \
		}                                                                             \
                                                                                      \
		if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT))                \
		{                                                                             \
			/* WAIT or FAULT response */                                              \
			if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) \
			{                                                                         \
				for (n = 32U + 1U; n; n--)                                            \
				{                                                                     \
					SW_CLOCK_CYCLE(); /* Dummy Read RDATA[0:31] + Parity */           \
				}                                                                     \
			}                                                                         \
			/* Turnaround */                                                          \
			for (n = DAP_Data.swd_conf.turnaround; n; n--)                            \
			{                                                                         \
				SW_CLOCK_CYCLE();                                                     \
			}                                                                         \
			PIN_SWDIO_OUT_ENABLE();                                                   \
			if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) \
			{                                                                         \
				PIN_SWDIO_OUT(0U);                                                    \
				for (n = 32U + 1U; n; n--)                                            \
				{                                                                     \
					SW_CLOCK_CYCLE(); /* Dummy Write WDATA[0:31] + Parity */          \
				}                                                                     \
			}                                                                         \
			PIN_SWDIO_OUT(1U);                                                        \
			return ((uint8_t)ack);                                                    \
		}                                                                             \
                                                                                      \
		/* Protocol error */                                                          \
		for (n = DAP_Data.swd_conf.turnaround + 32U + 1U; n; n--)                     \
		{                                                                             \
			SW_CLOCK_CYCLE(); /* Back off data phase */                               \
		}                                                                             \
		PIN_SWDIO_OUT_ENABLE();                                                       \
		PIN_SWDIO_OUT(1U);                                                            \
		return ((uint8_t)ack);                                                        \
	}

#undef PIN_DELAY
#define PIN_DELAY() PIN_DELAY_FAST()
SWD_TransferFunction(Fast);

#undef PIN_DELAY
#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)
SWD_TransferFunction(Slow);

// SWD Transfer I/O
//   request: A[3:2] RnW APnDP
//   data:    DATA[31:0]
//   return:  ACK[2:0]
uint8_t SWD_Transfer(uint32_t request, uint32_t *data)
{
	if (DAP_Data.fast_clock)
	{
		return SWD_TransferFast(request, data);
	}
	else
	{
		return SWD_TransferSlow(request, data);
	}
}

#endif /* (DAP_SWD != 0) */
